Make DND work better with shaped windows
authorMatthias Clasen <mclasen@redhat.com>
Fri, 5 Feb 2010 00:15:54 +0000 (19:15 -0500)
committerTristan Van Berkom <tristan.van.berkom@gmail.com>
Sun, 4 Apr 2010 00:56:05 +0000 (20:56 -0400)
If mouse clicks go through, drag-and-drop should too...
Fixes bug 608615.

gdk/x11/gdkdisplay-x11.c
gdk/x11/gdkdisplay-x11.h
gdk/x11/gdkdnd-x11.c
gdk/x11/gdkprivate-x11.h
gdk/x11/gdkwindow-x11.c

index 2b10a93618ab9df66cb0ef4d548cb2f43227da2a..c0cbc99f523037e1d4c25a397be6ef9dae73a43f 100644 (file)
@@ -280,10 +280,10 @@ gdk_display_open (const gchar *display_name)
   display_x11->have_shapes = FALSE;
   display_x11->have_input_shapes = FALSE;
 
-  if (XShapeQueryExtension (GDK_DISPLAY_XDISPLAY (display), &ignore, &ignore))
+  if (XShapeQueryExtension (GDK_DISPLAY_XDISPLAY (display), &display_x11->shape_event_base, &ignore))
     {
       display_x11->have_shapes = TRUE;
-#ifdef ShapeInput            
+#ifdef ShapeInput
       if (XShapeQueryVersion (GDK_DISPLAY_XDISPLAY (display), &maj, &min))
        display_x11->have_input_shapes = (maj == 1 && min >= 1);
 #endif
index 2873c2d67482d593552b4248cb0bcafea9e015cb..f8a55d0103b3cb12b2ca4b02c11eb2bebadd9e29 100644 (file)
@@ -147,6 +147,7 @@ struct _GdkDisplayX11
 
   guint have_shapes : 1;
   guint have_input_shapes : 1;
+  gint shape_event_base;
 
   /* Alpha mask picture format */
   XRenderPictFormat *mask_format;
index 938e7e14da55b0073164d4b44b6bd376e0f4b713..2da9567b61db2ee3e7a8c52e74c77e4cfe0ae152 100644 (file)
@@ -27,6 +27,8 @@
 #include "config.h"
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+
 #include <string.h>
 
 #include "gdk.h"          /* For gdk_flush() */
@@ -53,6 +55,9 @@ typedef struct {
   guint32 xid;
   gint x, y, width, height;
   gboolean mapped;
+  gboolean shape_selected;
+  gboolean shape_valid;
+  GdkRegion *shape;
 } GdkCacheChild;
 
 typedef struct {
@@ -308,6 +313,23 @@ precache_target_list (GdkDragContext *context)
 
 /* Utility functions */
 
+static void
+free_cache_child (GdkCacheChild *child,
+                  GdkDisplay    *display)
+{
+  if (child->shape)
+    gdk_region_destroy (child->shape);
+
+  if (child->shape_selected && display)
+    {
+      GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+      XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
+    }
+
+  g_free (child);
+}
+
 static void
 gdk_window_cache_add (GdkWindowCache *cache,
                      guint32 xid,
@@ -322,12 +344,50 @@ gdk_window_cache_add (GdkWindowCache *cache,
   child->width = width;
   child->height = height;
   child->mapped = mapped;
+  child->shape_selected = FALSE;
+  child->shape_valid = FALSE;
+  child->shape = NULL;
 
   cache->children = g_list_prepend (cache->children, child);
   g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid), 
                       cache->children);
 }
 
+static GdkFilterReturn
+gdk_window_cache_shape_filter (GdkXEvent *xev,
+                               GdkEvent  *event,
+                               gpointer   data)
+{
+  XEvent *xevent = (XEvent *)xev;
+  GdkWindowCache *cache = data;
+
+  GdkDisplayX11 *display = GDK_DISPLAY_X11 (gdk_screen_get_display (cache->screen));
+
+  if (display->have_shapes &&
+      xevent->type == display->shape_event_base + ShapeNotify)
+    {
+      XShapeEvent *xse = (XShapeEvent*)xevent;
+      GList *node;
+
+      node = g_hash_table_lookup (cache->child_hash,
+                                  GUINT_TO_POINTER (xse->window));
+      if (node)
+        {
+          GdkCacheChild *child = node->data;
+          child->shape_valid = FALSE;
+          if (child->shape)
+            {
+              gdk_region_destroy (child->shape);
+              child->shape = NULL;
+            }
+        }
+
+      return GDK_FILTER_REMOVE;
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
 static GdkFilterReturn
 gdk_window_cache_filter (GdkXEvent *xev,
                         GdkEvent  *event,
@@ -403,10 +463,13 @@ gdk_window_cache_filter (GdkXEvent *xev,
                                    GUINT_TO_POINTER (xdwe->window));
        if (node) 
          {
+           GdkCacheChild *child = node->data;
+
            g_hash_table_remove (cache->child_hash,
                                 GUINT_TO_POINTER (xdwe->window));
            cache->children = g_list_remove_link (cache->children, node);
-           g_free (node->data);
+           /* window is destroyed, no need to disable ShapeNotify */
+           free_cache_child (child, NULL);
            g_list_free_1 (node);
          }
        break;
@@ -434,7 +497,7 @@ gdk_window_cache_filter (GdkXEvent *xev,
 
        node = g_hash_table_lookup (cache->child_hash, 
                                    GUINT_TO_POINTER (xume->window));
-       if (node) 
+       if (node)
          {
            GdkCacheChild *child = node->data;
            child->mapped = FALSE;
@@ -486,6 +549,7 @@ gdk_window_cache_new (GdkScreen *screen)
   XSelectInput (xdisplay, GDK_WINDOW_XWINDOW (root_window),
                result->old_event_mask | SubstructureNotifyMask);
   gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
+  gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
 
   if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
                                       GDK_WINDOW_XWINDOW (root_window),
@@ -514,14 +578,57 @@ gdk_window_cache_destroy (GdkWindowCache *cache)
                GDK_WINDOW_XWINDOW (root_window),
                cache->old_event_mask);
   gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
+  gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
 
-  g_list_foreach (cache->children, (GFunc)g_free, NULL);
+  g_list_foreach (cache->children, (GFunc)free_cache_child, 
+      gdk_screen_get_display (cache->screen));
   g_list_free (cache->children);
   g_hash_table_destroy (cache->child_hash);
 
   g_free (cache);
 }
 
+static gboolean
+is_pointer_within_shape (GdkDisplay    *display,
+                         GdkCacheChild *child,
+                         gint           x_pos,
+                         gint           y_pos)
+{
+  if (!child->shape_selected)
+    {
+      GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+      XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
+      child->shape_selected = TRUE;
+    }
+  if (!child->shape_valid)
+    {
+      GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+      GdkRegion *input_shape;
+
+      child->shape = _xwindow_get_shape (display_x11->xdisplay,
+                                         child->xid, ShapeBounding);
+#ifdef ShapeInput
+      input_shape = _xwindow_get_shape (display_x11->xdisplay,
+                                        child->xid, ShapeInput);
+      if (child->shape && input_shape)
+        {
+          gdk_region_intersect (child->shape, input_shape);
+          gdk_region_destroy (input_shape);
+        }
+      else if (input_shape)
+        {
+          child->shape = input_shape;
+        }
+#endif
+
+      child->shape_valid = TRUE;
+    }
+
+  return child->shape == NULL ||
+         gdk_region_point_in (child->shape, x_pos, y_pos);
+}
+
 static Window
 get_client_window_at_coords_recurse (GdkDisplay *display,
                                     Window      win,
@@ -594,19 +701,28 @@ get_client_window_at_coords (GdkWindowCache *cache,
       GdkCacheChild *child = tmp_list->data;
 
       if ((child->xid != ignore) && (child->mapped))
-       {
-         if ((x_root >= child->x) && (x_root < child->x + child->width) &&
-             (y_root >= child->y) && (y_root < child->y + child->height))
-           {
-             retval = get_client_window_at_coords_recurse (gdk_screen_get_display (cache->screen),
-                                                           child->xid, TRUE,
-                                                           x_root - child->x,
-                                                           y_root - child->y);
-             if (!retval)
-               retval = child->xid;
-           }
-         
-       }
+        {
+          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
+              (y_root >= child->y) && (y_root < child->y + child->height))
+            {
+              GdkDisplay *display = gdk_screen_get_display (cache->screen);
+
+              if (!is_pointer_within_shape (display, child,
+                                            x_root - child->x,
+                                            y_root - child->y))
+                {
+                  tmp_list = tmp_list->next;
+                  continue;
+                }
+
+              retval = get_client_window_at_coords_recurse (display,
+                  child->xid, TRUE,
+                  x_root - child->x,
+                  y_root - child->y);
+              if (!retval)
+                retval = child->xid;
+            }
+        }
       tmp_list = tmp_list->next;
     }
 
index bfbf3d9bdd5a4eb5250a7fd307ced2cda4e101b8..5eaf188ae2eff17a5aee8672457bf5722f0c1e77 100644 (file)
@@ -138,6 +138,10 @@ void     _gdk_x11_window_queue_translation (GdkWindow *window,
 void     _gdk_selection_window_destroyed   (GdkWindow            *window);
 gboolean _gdk_selection_filter_clear_event (XSelectionClearEvent *event);
 
+GdkRegion* _xwindow_get_shape              (Display *xdisplay,
+                                            Window window,
+                                            gint shape_type);
+
 void     _gdk_region_get_xrectangles       (const GdkRegion      *region,
                                             gint                  x_offset,
                                             gint                  y_offset,
index 9c46716444e66cb7a6182fff714891fb0645823d..c6d608cd28b44817d5adb8d013257c10933d3c31 100644 (file)
@@ -4593,10 +4593,10 @@ gdk_window_set_functions (GdkWindow    *window,
   gdk_window_set_mwm_hints (window, &hints);
 }
 
-static GdkRegion *
-xwindow_get_shape (Display *xdisplay,
-                  Window window,
-                  gint shape_type)
+GdkRegion *
+_xwindow_get_shape (Display *xdisplay,
+                   Window window,
+                   gint shape_type)
 {
   GdkRegion *shape;
   GdkRectangle *rl;
@@ -4658,7 +4658,7 @@ _gdk_windowing_get_shape_for_mask (GdkBitmap *mask)
                     GDK_PIXMAP_XID (mask),
                     ShapeSet);
   
-  region = xwindow_get_shape (GDK_DISPLAY_XDISPLAY (display),
+  region = _xwindow_get_shape (GDK_DISPLAY_XDISPLAY (display),
                              window, ShapeBounding);
 
   XDestroyWindow (GDK_DISPLAY_XDISPLAY (display),
@@ -4672,7 +4672,7 @@ _gdk_windowing_window_get_shape (GdkWindow *window)
 {
   if (!GDK_WINDOW_DESTROYED (window) &&
       gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
-    return xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
+    return _xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
                              GDK_WINDOW_XID (window), ShapeBounding);
 
   return NULL;
@@ -4684,7 +4684,7 @@ _gdk_windowing_window_get_input_shape (GdkWindow *window)
 #if defined(ShapeInput)
   if (!GDK_WINDOW_DESTROYED (window) &&
       gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
-    return xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
+    return _xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
                              GDK_WINDOW_XID (window),
                              ShapeInput);
 #endif